Conv2DBackpropInputFusion ============================= 计算二维卷积对输入的反向梯度(input gradient)。 ``dy``、``dx`` 为 **NHWC**;``w`` 为卷积核布局(见下);支持普通卷积、Depthwise,以及满足条件时的 1x1 优化路径。 对组 :math:`g`、输出位置 :math:`(o_h, o_w)`、核偏移 :math:`(k_h, k_w)`,令 :math:`C_o' = C_{out}/G`,:math:`C_i' = C_{in}/G`。对应输入坐标为: .. math:: \begin{aligned} i_h &= o_h \cdot s_h + k_h \cdot d_h - pad_u \\ i_w &= o_w \cdot s_w + k_w \cdot d_w - pad_l \end{aligned} 当 :math:`(i_h, i_w)` 落在输入有效范围内时累加,否则该项为 0。 实现上:普通路径对 ``dy`` 与 ``w`` 做 GEMM(GEneral Matrix Multiply,通用矩阵乘), 再经 col2im(column to image,列展开还原成特征图)累加到 ``dx``; Depthwise 与 1x1(无 pad、dilation/stride 为 1、group 为 1)走专用路径。 输入: - **dy** - 输出梯度地址,形状 [batch, out_h, out_w, out_channel](NHWC) - **w** - 权重地址,形状 [out_channel, in_channel/group, kernel_h, kernel_w] (OIHW,不是 OHWI / NHWC) - **conv_param** - ConvParameter 结构体地址,字段见下 - **core_mask** - 核掩码(仅共享存储版本使用) **ConvParameter 定义:** .. code-block:: c :linenos: typedef struct ConvParameter { void* workspace_; // 工作区指针;实际分配字节数见下方警告(常大于 buffer_size_) int output_batch_; // dy 的 batch int input_batch_; // dx 的 batch(通常与 output_batch_ 相同) int input_h_; // 输入高(dx) int input_w_; // 输入宽(dx) int output_h_; // 输出梯度高(dy) int output_w_; // 输出梯度宽(dy) int input_channel_; // 输入通道(dx) int output_channel_; // 输出通道(dy) int kernel_h_; // 卷积核高 int kernel_w_; // 卷积核宽 int group_; // 组数,1 为普通卷积 int pad_l_; // 左填充 int pad_u_; // 上填充 int dilation_h_; // 高方向膨胀 int dilation_w_; // 宽方向膨胀 int stride_h_; // 高方向步长 int stride_w_; // 宽方向步长 int buffer_size_; // 分块预算,单位字节(非元素个数);_s/_p 都读此字段。见下方警告。 int pad_d_; // 下填充 int pad_r_; // 右填充 int nweights_; // 权重 w 的元素总数 } ConvParameter; 输出: - **dx** - 输入梯度地址,形状 ``[batch, in_h, in_w, in_channel]``; 调用前通常先清零,再由算子累加写入 支持平台: ``FT78NE`` ``MT7004`` .. warning:: MT7004的workspace\_ / buffer_size\_ 分配算法(与前向 conv2d / transpose / backprop_filter 都不同): 1. 权重布局是 OIHW [C_out, C_in/G, K_h, K_w],不是前向常见的 OHWI。传错布局结果静默错误。 2. 必须填对 nweights\_ = out_channel × (in_channel / group) × kernel_h × kernel_w, 且与 w 实际元素数一致;组卷积按 nweights\_/group 切各组权重起始。 3. buffer_size\_ 是字节(不是元素个数)。内部用 buffer_size\_/2 估半区;部分汇编按 float 个数做指针算术,因此 workspace\_ 须 ≥ buffer_size\_ × sizeof(float)。字段与分配字节可以不相等。 4. 共享 \_s 与私有 \_p 都会读 buffer_size\_ / workspace\_ (不像前向 conv \_s 忽略字段)。无 transpose 那种 packed 权重 + core_num×2048 的额外 DDR scratch 规则; 也无 filter 那种 core_num×dw partial 规则。常用:: buffer_size_ = max(2048, 2 * Kh * Kw * (Cin / G) * sizeof(dtype)); workspace_bytes = buffer_size_ * sizeof(float); 5. 测试常用 buffer_size\_ = 0x10000。 .. note:: - FT78NE 支持 fp32 - MT7004 支持 fp16、fp32 - Depthwise 条件:output_channel、group、input_channel 三者相等,且 dilation 为 1 - 1x1 优化条件:Kh=Kw=1,四向 pad 为 0,dilation/stride 为 1,group 为 1 **共享存储版本:** .. c:function:: void hp_conv2d_backprop_input_s(float16 *dy, float16 *w, float16 *dx, ConvParameter *conv_param, int core_mask) .. c:function:: void fp_conv2d_backprop_input_s(float *dy, float *w, float *dx, ConvParameter *conv_param, int core_mask) **C调用示例:** .. code-block:: c :linenos: :emphasize-lines: 37 // MT7004 示例(共享存储多核,DDR 地址) void TestConvBackpropInputSMCFp32(int core_mask) { int core_id = get_core_id(); int logic_core_id = GetLogicCoreId(core_mask, core_id); int core_num = GetCoreNum(core_mask); float *dy = (float *)0x86000000; float *w = (float *)0x87000000; // OIHW: [16, 16, 1, 1] float *dx = (float *)0x88000000; ConvParameter *param = (ConvParameter *)0x89000000; float *workspace = (float *)0x8A000000; if (logic_core_id == 0) { param->workspace_ = workspace; // 字段填字节预算;实际 malloc 建议 >= buffer_size_ * sizeof(float) param->buffer_size_ = 0x10000; param->input_batch_ = 1; param->output_batch_ = 1; param->input_h_ = 4; param->input_w_ = 4; param->input_channel_ = 16; param->output_h_ = 4; param->output_w_ = 4; param->output_channel_ = 16; param->kernel_h_ = 1; param->kernel_w_ = 1; param->group_ = 1; param->pad_l_ = 0; param->pad_u_ = 0; param->pad_d_ = 0; param->pad_r_ = 0; param->dilation_h_ = 1; param->dilation_w_ = 1; param->stride_h_ = 1; param->stride_w_ = 1; param->nweights_ = 16 * 16 * 1 * 1; } sys_bar(0, core_num); fp_conv2d_backprop_input_s(dy, w, dx, param, core_mask); } void main() { int core_mask = 0b1111; TestConvBackpropInputSMCFp32(core_mask); } **私有存储版本:** .. c:function:: void hp_conv2d_backprop_input_p(float16 *dy, float16 *w, float16 *dx, ConvParameter *conv_param) .. c:function:: void fp_conv2d_backprop_input_p(float *dy, float *w, float *dx, ConvParameter *conv_param) **C调用示例:** .. code-block:: c :linenos: :emphasize-lines: 31 // MT7004 示例(私有存储单核,AM 地址) void TestConvBackpropInputAMFp32(void) { float *dy = (float *)0x10000000; float *w = (float *)0x10010000; // OIHW: [16, 16, 1, 1] float *dx = (float *)0x10020000; ConvParameter *param = (ConvParameter *)0x10030000; float *workspace = (float *)0x10040000; param->workspace_ = workspace; // 字段填字节预算;workspace 建议至少 buffer_size_ * sizeof(float) param->buffer_size_ = 0x10000; param->input_batch_ = 1; param->output_batch_ = 1; param->input_h_ = 4; param->input_w_ = 4; param->input_channel_ = 16; param->output_h_ = 4; param->output_w_ = 4; param->output_channel_ = 16; param->kernel_h_ = 1; param->kernel_w_ = 1; param->group_ = 1; param->pad_l_ = 0; param->pad_u_ = 0; param->pad_d_ = 0; param->pad_r_ = 0; param->dilation_h_ = 1; param->dilation_w_ = 1; param->stride_h_ = 1; param->stride_w_ = 1; param->nweights_ = 16 * 16 * 1 * 1; fp_conv2d_backprop_input_p(dy, w, dx, param); } void main() { TestConvBackpropInputAMFp32(); }